﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using LibrarySync2.Metadata;
using LibrarySync2.Common.Interfaces;
using LibrarySync2.Common.Extensions;
using LibrarySync2.Common.Xml;
using System.ComponentModel;
using LibrarySync2.Common.Event;
using LibrarySync2.Common.Enum;

namespace LibrarySync2.Business.FileSystem.Item
{
    [Serializable]
    public class MediaFile : File, IMediaFile
    {
        protected const int HorizontalResolution720p = 1280;
        protected const int HorizontalResolution480p = 720;

        public string Id { get; set; }
        public string CanonicalName { get; set; }

        public int Width { get; set; }
        public int Height { get; set; }
        public int VideoBitRate { get; set; }
        public int AudioBitRate { get; set; }
        public int RunTime { get; set; }
        public bool HasSubtitleStreams { get; set; }

        public bool IsSelected { get; set; }

        public string[] FullDescription
        {
            get
            {
                List<string> resultList = new List<string>();
                resultList.Add("File");
                resultList.Add(string.Format("Name:\t\t\t{0}", Name));
                resultList.Add(string.Format("Full Name\t\t\t{0}", FullName));
                resultList.Add(string.Format("File Size\t\t\t{0} Bytes", FileSize));
                resultList.Add(string.Format("Created Date\t\t{0}", CreatedDate.ToString("MM/dd/yyyy HH:mm:ss")));
                resultList.Add(string.Format("Resolution\t\t{0} x {1}", Width, Height));
                resultList.Add(string.Format("TMDB ID\t\t\t{0}", Id));
                return resultList.ToArray();
            }
        }

        public bool Is1080p
        {
            //We base this on horizontal resolution to help 
            //mitigate aspect ratio cropping.
            get { return Width > HorizontalResolution720p; }
        }

        public bool Is720p
        {
            //We base this on horizontal resolution to help 
            //mitigate aspect ratio cropping.
            get { return Width > HorizontalResolution480p && Width <= HorizontalResolution720p; }
        }

        public string FormattedName
        {
            get
            {
                const string The = "The ";
                const string ForbiddenChars = "\"*/:<>?\\|";

                if (string.IsNullOrEmpty(CanonicalName))
                    return Name;

                var result = CanonicalName;
                var extension = Name.Substring(Name.LastIndexOf('.'));
                result = result.Substring(0, result.Length);

                if (result.StartsWith(The, StringComparison.OrdinalIgnoreCase))
                {
                    result = result.Substring(The.Length) + ", " + The.Trim();
                }

                var forbiddenChars = ForbiddenChars.ToCharArray();
                foreach (var fChar in forbiddenChars)
                {
                    result = result.Replace(fChar.ToString(), "");
                }

                return result.Trim() + extension;
            }
        }

        public MediaFile()
            : base()
        {
            //Do nothing.  Deserializing.
        }

        internal MediaFile(FileInfo fileInfo, string root, IMetadataProvider metadataProvider)
            : base(fileInfo, root)
        {
            metadataProvider.RetrieveMetadata(this);
        }

        public long CalculateFullSize()
        {
            return Item.Directory.CalculateSizeFromRoot(Root);
        }

        public bool IsBetterThan(IMediaFile matchingFile)
        {
            if (matchingFile == null)
                throw new ArgumentNullException("matchingFile");

            //Resolution is king.
            if (Is1080p && !matchingFile.Is1080p)
                return true;

            if (Is720p && !matchingFile.Is720p && !matchingFile.Is1080p)
                return true;

            //Check for sub streams.  No streams means we've pruned unnecessary
            //streams or burnt them in.  SUB STREAMS ARE A SIGN OF WEAKNESS!  :)
            if (!HasSubtitleStreams && matchingFile.HasSubtitleStreams)
                return true;

            //Reasonably equivalent.  Don't bother copying.
            return false;
        }

        protected override void WriteElementsToXml(XmlWriter writer)
        {
            base.WriteElementsToXml(writer);
            writer.WriteElementString("Id", Id);
            writer.WriteElementString("CanonicalName", CanonicalName);
            writer.WriteElementString("Width", Width.ToString());
            writer.WriteElementString("Height", Height.ToString());
            writer.WriteElementString("VideoBitRate", VideoBitRate.ToString());
            writer.WriteElementString("AudioBitRate", AudioBitRate.ToString());
            writer.WriteElementString("RunTime", RunTime.ToString());
            writer.WriteElementString("HasSubtitleStreams", HasSubtitleStreams.ToString());
        }

        protected override void ReadElementsFromXml(XmlNode itemNode)
        {
            base.ReadElementsFromXml(itemNode);
            Id = itemNode.GetNodeValue("Id");
            CanonicalName = itemNode.GetNodeValue("CanonicalName");
            Width = itemNode.GetNodeValueAs<int>("Width", XmlExtensionMethods.ParseInt).Value;
            Height = itemNode.GetNodeValueAs<int>("Height", XmlExtensionMethods.ParseInt).Value;
            VideoBitRate = itemNode.GetNodeValueAs<int>("VideoBitRate", XmlExtensionMethods.ParseInt).Value;
            AudioBitRate = itemNode.GetNodeValueAs<int>("AudioBitRate", XmlExtensionMethods.ParseInt).Value;
            RunTime = itemNode.GetNodeValueAs<int>("RunTime", XmlExtensionMethods.ParseInt).Value;
            HasSubtitleStreams = itemNode.GetNodeValueAs<bool>("HasSubtitleStreams", XmlExtensionMethods.ParseBoolean).Value;
        }

        public void CopyToDestination(BackgroundWorker copyWorker, EventHandler<CopyEventArgs> copyEvent,
            MediaFileUpdateType mediaFileUpdateType, DirectoryInfo destination, bool includeMetadata)
        {
            //Make sure the file to copy exists.
            var fileInfo = new FileInfo(FullName);
            if (!fileInfo.Exists)
                return;

            destination.CreateSubdirectories(Root, RelativeDirectoryPath, includeMetadata);

            string destinationPath = string.Concat(destination.FullName, Path.DirectorySeparatorChar.ToString(), RelativePath);
            PerformCopy(destinationPath, fileInfo, copyWorker, copyEvent);
        }
    }
}
